package mymongo

import (
    "fmt"
    "context"
    "time"
    "mygo/mystruct"
    "go.mongodb.org/mongo-driver/bson"
    "go.mongodb.org/mongo-driver/mongo/options"
    "go.mongodb.org/mongo-driver/bson/primitive"
)

/* =================================================================================
   数据表操作
*/
func Append_One(data map[string]interface{}, client *Curs) *mystruct.Retdate {
	results := &mystruct.Retdate{"Insert", mystruct.MyCodeOk, "succeed"}

	send_data := mystruct.Map_data{data, time.Now()}
	ret, err := client.Cur.InsertOne(context.TODO(), send_data)
	if err != nil {
	 	results.Mesg = err.Error()
	 	results.Code = mystruct.MyCodeErr
	}else{
		oid, _ := ret.InsertedID.(primitive.ObjectID)
		results.Mesg = oid.Hex()
	}
	return results
}

func Append_All(data []interface{}, client *Curs) *mystruct.Retdate {
	results := &mystruct.Retdate{"Insert", mystruct.MyCodeOk, "succeed"}

	opts := options.InsertMany().SetOrdered(false)
	_, err := client.Cur.InsertMany(context.TODO(), data, opts)
	if err != nil {
		results.Code = mystruct.MyCodeErr
		results.Mesg = err.Error()
	}
	return results
}


func Update_One(data map[string]interface{}, client *Curs, id string) *mystruct.Retdate {
	results := &mystruct.Retdate{"Update", mystruct.MyCodeOk, "succeed"}

	//opts := options.Update().SetUpsert(true) //没有则添加
	OjbId, _ := primitive.ObjectIDFromHex(id)
	bson_data := bson.D{}
    search_build(data, &bson_data, client, "2")
	update := bson.D{{"$set", bson_data}}
	ret, err := client.Cur.UpdateOne(context.TODO(), bson.M{"_id": OjbId}, update)
	results.Code = ret.ModifiedCount
	if err != nil {
	 	results.Mesg = err.Error()
	}else{
		results.Mesg = fmt.Sprintf("%v", ret.ModifiedCount)
	}
	return results
}

func Update_Field(field string, value interface{}, client *Curs, id string) *mystruct.Retdate {
	results := &mystruct.Retdate{"Update", mystruct.MyCodeOk, "succeed"}

	OjbId, _ := primitive.ObjectIDFromHex(id)
	bson_data := bson.D{}
	bson_data = append(bson_data, bson.E{Key: fmt.Sprintf("data.%s", field), Value: value})
	update := bson.D{{"$set", bson_data}}
	ret, err := client.Cur.UpdateOne(context.TODO(), bson.M{"_id": OjbId}, update)
	results.Code = ret.ModifiedCount
	if err != nil {
	 	results.Mesg = err.Error()
	}else{
		results.Mesg = fmt.Sprintf("%v", ret.ModifiedCount)
	}
	return results
}

func Delete_One(id string, client *Curs) *mystruct.Retdate {
	results := &mystruct.Retdate{"Delete", mystruct.MyCodeOk, "succeed"}
	
	OjbId, _ := primitive.ObjectIDFromHex(id)
	ret, err := client.Cur.DeleteOne(context.TODO(), bson.M{"_id": OjbId})
	results.Code = ret.DeletedCount
	if err != nil {
	 	results.Mesg = err.Error()
	}else{
		results.Mesg = fmt.Sprintf("%v", ret.DeletedCount)
	}
	return results
}

func Delete_All(data map[string]interface{}, client *Curs) *mystruct.Retdate {
	results := &mystruct.Retdate{"Delete", mystruct.MyCodeOk, "succeed"}

	collation := &options.Collation{
    	Locale:          "zh", // 适用于中文和英文的基本排序规则
    	Strength:        2,       // 增强区分大小写，对于英文部分有效
	}
	bson_data := bson.D{}
    search_build(data, &bson_data, client, "2")
	deleteResult, err := client.Cur.DeleteMany(context.TODO(), bson_data, options.Delete().SetCollation(collation))
	if err != nil {
		results.Code = mystruct.MyCodeErr
		results.Mesg = err.Error()
	}else{
		results.Mesg = fmt.Sprintf("[ %v ]", deleteResult.DeletedCount)
	}
	return results
}

func Wacth_One(id string, client *Curs) (*mystruct.Retdate, bson.M) {
	results := &mystruct.Retdate{"WacthData", mystruct.MyCodeOk, "succeed"}
	
	OjbId, _ := primitive.ObjectIDFromHex(id)
	ret := client.Cur.FindOne(context.TODO(), bson.M{"_id": OjbId})
	var result bson.M
	if err := ret.Decode(&result); err != nil {
		results.Mesg = err.Error()
		results.Code = mystruct.MyCodeErr
	}
	return results, result
}

func Wacth_All(data map[string]interface{}, limit *mystruct.Mypage, client *Curs) (*mystruct.Retdate, []primitive.M) {
	results := &mystruct.Retdate{"WacthData", mystruct.MyCodeOk, "succeed"}
	
	// 构建基础搜索条件（不包含分页的_id条件）
	search := bson.D{}
	statStr, ok1 := data["start_time"].(string)
	stopStr, ok2 := data["stop_time"].(string)
	
	// 处理时间条件
	if ok1 && ok2 && statStr != "" && stopStr != "" {
		stat, _ := time.Parse("2006-01-02-15-04-05", statStr)
		stop, _ := time.Parse("2006-01-02-15-04-05", stopStr)
		search = append(search, bson.E{Key :"appendtime", Value: bson.D{
			{"$gt", stat.Add(-8 * time.Hour)},
			{"$lte", stop.Add(-8 * time.Hour)},
		}})
	}
	delete(data, "start_time") 
	delete(data, "stop_time")
	// 处理其他条件
	search_build(data, &search, client, "0")

	// 预查询：获取起始文档（应用所有条件，除分页的_id）
    skip := (limit.Page) * limit.Limit
    opts := options.FindOne().SetSort(bson.D{{"_id", client.Body.Sort}}).SetSkip(int64(skip))
    var startDoc mystruct.SearchItem
    if err := client.Cur.FindOne(context.TODO(), search, opts).Decode(&startDoc); err != nil {
        results.Mesg = err.Error()
		results.Code = mystruct.MyCodeErr
		return results, nil
    }
    // 添加分页的_id条件到后续查询
	if client.Body.Sort == 1 {
		search = append(search, bson.E{Key: "_id", Value: bson.M{"$gt": startDoc.ID}})
	}else{
		search = append(search, bson.E{Key: "_id", Value: bson.M{"$lt": startDoc.ID}})
	}

	// 执行后续查询
	findOptions := options.Find()
	findOptions.SetLimit(limit.Limit-1)
	//findOptions.SetBatchSize(1000)
	//findOptions.SetMaxTime(30 * time.Second)
	findOptions.SetSort(bson.D{{"_id", client.Body.Sort}})
	ctx := context.TODO()
	res_cur, err := client.Cur.Find(ctx, search, findOptions)
	if err != nil {
		results.Mesg = err.Error()
		results.Code = mystruct.MyCodeErr
		return results, nil
	}
	defer res_cur.Close(ctx)

	var mapList []primitive.M
    startDocData := startDoc.Data
    startDocData["_id"] = startDoc.ID.Hex()
    mapList = append(mapList, startDocData)
    
    // 添加后续查询结果
	for res_cur.Next(ctx) {
        var result bson.M
        if err := res_cur.Decode(&result); err != nil {
            continue
        }
        data := result["data"].(primitive.M)
		data["_id"] = result["_id"].(primitive.ObjectID).Hex()
        mapList = append(mapList, data)
    }
	return results, mapList
}

func Wacth_Sum(data map[string]interface{}, client *Curs) (*mystruct.Retdate, int64) {
	results := &mystruct.Retdate{"WacthSum", mystruct.MyCodeOk, "succeed"}

	search := bson.D{}
	statStr, ok1 := data["start_time"].(string)
	stopStr, ok2 := data["stop_time"].(string)
	if ok1 && ok2 && statStr != "" && stopStr != "" {
		stat, _ := time.Parse("2006-01-02-15-04-05", statStr)
		stop, _ := time.Parse("2006-01-02-15-04-05", stopStr)
		search = append(search, bson.E{Key :"appendtime", Value: bson.D{{"$gt", stat.Add(-8 * time.Hour)},{"$lte", stop.Add(-8 * time.Hour)}}})
	}
	delete(data, "start_time") 
	delete(data, "stop_time")

	search_build(data, &search, client, "0")

	var sum int64
	var err error
	if len(search) == 0 {
		sum, err = client.Cur.EstimatedDocumentCount(context.TODO())
	} else {
		sum, err = client.Cur.CountDocuments(context.TODO(), search)
	}
	if err != nil {
        results.Code = mystruct.MyCodeErr
        results.Mesg = fmt.Sprintf("Failed to count documents: %v", err)
    }
	return results, sum
}